using System;
using System.Collections.Generic;

namespace lab05
{
    interface IMatrix
    {
        void SetValue(int i, int j, int value);
        int GetValue(int i, int j);
        int GetNumRows();
        int GetNumColumns();
        IMatrixIterator GetIterator();
    }

    public struct MatrixElement
    {
        public int I;
        public int J;
        public int Value;
    }

    abstract class Matrix : IMatrix
    {
        protected List<IVector> MatrixColumns;

        public virtual void SetValue(int i, int j, int value)
        {
            MatrixColumns[j].SetValue(i, value);
        }

        public virtual int GetValue(int i, int j)
        {
            return MatrixColumns[j].GetValue(i);
        }

        public virtual int GetNumRows()
        {
            return MatrixColumns[0].GetNumRows();
        }

        public virtual int GetNumColumns()
        {
            return MatrixColumns.Count;
        }

        public abstract IMatrixIterator GetIterator();
    }

    class DenseMatrix : Matrix
    {
        public DenseMatrix(int height, int width)
        {
            MatrixColumns = new List<IVector>(width);
            for (int i = 0; i < width; i++)
            {
                MatrixColumns.Add(new DenseVector(height));
            }
        }

        public override IMatrixIterator GetIterator()
        {
            return new DenseMatrixIterator(this);
        }
    }

    class SparseMatrix : Matrix
    {
        public SparseMatrix(int height, int width)
        {
            MatrixColumns = new List<IVector>(width);
            for (int i = 0; i < width; i++)
            {
                MatrixColumns.Add(new SparseVector(height));
            }
        }

        public override IMatrixIterator GetIterator()
        {
            return new SparseMatrixIterator(this);
        }
    }

    class MatrixRoutines
    {
        /*
        public static List<ICommand> Fill(IMatrix matrix, int numNonZeros, int minValue, int maxValue)
        {
            List<ICommand> result = new List<ICommand>();
            if (numNonZeros > matrix.GetNumColumns() * matrix.GetNumRows())
                throw new Exception("Too much numNonZeros");
            Random rnd = new Random();
            for (int z = 0; z < numNonZeros; z++)
            {
                bool isAdded = false;
                while (!isAdded)
                {
                    int i = rnd.Next(matrix.GetNumRows());
                    int j = rnd.Next(matrix.GetNumColumns());
                    int value = 1 + rnd.Next(10);
                    if (matrix.GetValue(i, j) != 0) continue;

                    MatrixChange cmd = new MatrixChange(matrix, i, j, value);

                    cmd.Execute();
                    result.Add(cmd);
                    isAdded = true;
                }
            }
            return result;
        }
        */

        public static void Print(IMatrix matrix)
        {
            for (int y = 0; y < matrix.GetNumRows(); y++)
            {
                for (int x = 0; x < matrix.GetNumColumns(); x++)
                {
                    Console.Write(@"{0,4}", matrix.GetValue(x, y));
                }
                Console.WriteLine();
            }
            Console.WriteLine();
        }
    }

    class MatrixStatistics
    {
        private readonly IMatrix _matrix;

        public MatrixStatistics(IMatrix m)
        {
            _matrix = m;
        }

        public int Sum()
        {
            int sum = 0;
            for (int i = 0; i < _matrix.GetNumRows(); i++)
            {
                for (int j = 0; j < _matrix.GetNumColumns(); j++)
                {
                    sum += _matrix.GetValue(i, j);
                }
            }
            return sum;
        }

        public int Nonz()
        {
            int nonz = 0;
            for (int x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (int y = 0; y < _matrix.GetNumRows(); y++)
                {
                    if (_matrix.GetValue(x, y) != 0) nonz++;
                }
            }
            return nonz;
        }

        public int Max()
        {
            int max = 0;
            for (int x = 0; x < _matrix.GetNumColumns(); x++)
            {
                for (int y = 0; y < _matrix.GetNumRows(); y++)
                {
                    int cur = _matrix.GetValue(x, y);
                    if (cur > max) max = cur;
                }
            }
            return max;
        }

        public double Middle()
        {
            return 1.0*Sum()/Nonz();
        }
    }

    class MatrixChange : ACommand
    {
        private IMatrix _matrix;
        public int I { get; set; }
        public int J { get; set; }
        public int Value { get; set; }


        public MatrixChange(IMatrix matrix, int i, int j, int newValue, ManagerCommand manager) : base(manager)
        {
            isActive = true;
            _matrix = matrix;
            I = i;
            J = j;
            Value = newValue;
        }

        public override void Execute()
        {
            doExecute();
            _matrix.SetValue(I, J, Value);
        }
    }

    class InitMatrix : ACommand
    {
        private IMatrix _matrix;
        private List<ICommand> _commands;

        public InitMatrix(IMatrix matrix, int nonZeros, int minValue, int maxValue, ManagerCommand manager) : base(manager)
        {
            _matrix = matrix;
            isActive = true;

            _commands = new List<ICommand>();
            if (nonZeros > matrix.GetNumColumns() * matrix.GetNumRows())
                throw new Exception("Too much numNonZeros");
            Random rnd = new Random();
            for (int z = 0; z < nonZeros; z++)
            {
                bool isAdded = false;
                while (!isAdded)
                {
                    isAdded = true;
                    int i = rnd.Next(matrix.GetNumRows());
                    int j = rnd.Next(matrix.GetNumColumns());
                    int value = minValue + rnd.Next(maxValue - minValue);

                    foreach (MatrixChange cmd in _commands)
                    {
                        if (cmd.I == i && cmd.J == j) isAdded = false;
                    }
                    if (!isAdded) continue;

                    MatrixChange cmmd = new MatrixChange(matrix, i, j, value, manager);
                    _commands.Add(cmmd);
                    cmmd.Execute();
                    isAdded = true;
                }
            }
        }

        public override void Execute()
        {
            doExecute();
            foreach (ACommand command in _commands)
            {
                command.Execute();
            }
        }
    }

}